package semantico;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Stack;
import java.util.Vector;


import sintatico.ErroSintatico;
import sintatico.SyntaticAnalyzer;
import symbols.Simbolo;
import symbols.Tag;
import symbols.Type;

public class SemanticAnalyzer {
	
	public Stack<Simbolo> tabelaSimbolos;
	
	public Stack<String> escopoAtual;
	
	public Stack<String> idAtual;
	
	public SyntaticAnalyzer sintatico;
	
	public StringBuffer programaOutput;
	
	public Type tipoAtual;
	
	private Integer ifCount = 0;
	
	private Integer whileCount = 0;

	private Vector<Simbolo> listaArgumentos;

	private int tamanhoVetor;

	private String nextLabel;

	private boolean isVetor;
	
	private Stack<String> pilhaOperandos;

	private Stack<String> pilhaOperadores;

	private int tipoOperacao;
	
	private String operando1;

	private String operando2;
	
	private int contador_operando_tmp;

	private Stack<Memoria> memoriaUsada;

	private Memoria memoria;

	private Stack<String> pilhaControleIf;

	private Stack<String> pilhaControleWhile;

	private boolean isPrint;

	private Type tipoRetorno;

	private boolean hasParameters;

	private String nomeFuncao;

	private boolean isInput;

	private String chamaFuncao;

	private int chamaFuncaoCont;

	private Stack<String> pilhaChamadaFuncao;

	private String fileName;

	public SemanticAnalyzer(SyntaticAnalyzer sintatico, String fileName) {
		this.fileName = fileName;
		this.sintatico = sintatico;
		this.tabelaSimbolos = new Stack<Simbolo>();
		this.escopoAtual = new Stack<String>();
		this.idAtual = new Stack<String>();
		this.nextLabel = "";
		this.isVetor = false;
		this.programaOutput = new StringBuffer();
		pilhaOperadores = new Stack<String>();
		pilhaOperandos = new Stack<String>();
		memoriaUsada = new Stack<Memoria>();
		pilhaControleWhile = new Stack<String>();
		pilhaControleIf = new Stack<String>();
		pilhaChamadaFuncao = new Stack<String>();
		ifCount = 0;
		chamaFuncaoCont = 0;
		whileCount = 0;
		isPrint = false;
		tipoRetorno = null;
		hasParameters = false;
		listaArgumentos = new Vector<Simbolo>();
	}

	private void geraCodigo(String line) {
		programaOutput.append(line);
		programaOutput.append("\n");
	}
	
	/**
	 * Escreve o inicio do programa no arquivo de saida
	 */
	public void inicioPrograma() {
		geraCodigo("@ /0000");
		geraCodigo("JP PROGRAM");
	}

	public void terminaPrograma() {
		geraCodigo("HM /0");
		declaraVariaveis();
//		geraCodigo("#");
	}

	public void fazerAcaoSemantica(Integer acaoSemantica) {
		Simbolo sym = null;
		String escAtual = "";
		String nextLexeme;
		String numeroAtual;

		if (acaoSemantica == null) {
			return;
		}
		else {
//			geraCodigo("Fez Acao Semantica: " + acaoSemantica);
//			System.out.println("Fez Acao Semantica: " + acaoSemantica);
			switch(acaoSemantica) {
			/************************************************************
			 * Executa as acoes semanticas para a submaquina "Programa"
			 ************************************************************/
			case 1: // inicio do programa
				geraCodigo("PROGRAM LV /0");
				break;
			case 2: // pegou "{"
				escopoAtual.push("PROGRAM");
				break;
			case 4: // pegou "}" (acaba escopo principal)
			case 5: // pegou ";" (acaba programa total)
				// Fim do programa
				// gerar codigo
				break;
			
			/************************************************************
			 * Executa as acoes semanticas para a submaquina "Comando"
			 ************************************************************/
			/*
			 * comando-atribuicao
			 */
			case 200: // pega um id para atribuicao
//				geraCodigo(nextLabel + " ");
				if (getSimboloByName(sintatico.getToken().getLexeme()) != null) {
					sym = getSimboloByName(sintatico.getToken().getLexeme());
				}
				else {
					new ErroSemantico("Variavel "+sintatico.getToken().getLexeme()+" nao declarada.", sintatico.getLexico().getLineNumber(),this);
				}
				idAtual.push(sym.getIdentificador());
				break;
			case 219: // Final da atribuicao
				if (isPrint) {
					if (isNumeric(pilhaOperandos.peek())) {
						String nomeVar = pilhaOperandos.pop();
						geraCodigo("LV /" + nomeVar);
					}
					else {
						String nomeVar = pilhaOperandos.pop();
						geraCodigo("LD " + nomeVar);
					}
					geraCodigo("+	OFFSET");
					geraCodigo("MM	RESULT_ASC");
					geraCodigo("LD	STRING_1");
					geraCodigo("PD	/100	;");
					geraCodigo("LD	STRING_2");
					geraCodigo("PD	/100	;");
					geraCodigo("LD	STRING_3");
					geraCodigo("PD	/100	;");
					geraCodigo("LD	STRING_4");
					geraCodigo("PD	/100	;");
					geraCodigo("LD	STRING_5");
					geraCodigo("PD	/100	;");
					geraCodigo("LD	STRING_6");
					geraCodigo("PD	/100	;");
					geraCodigo("LD	STRING_7");
					geraCodigo("PD	/100	;");
					geraCodigo("LD	STRING_6");
					geraCodigo("LD	RESULT_ASC");
					geraCodigo("PD  /100");
					geraCodigo("LD	NEW_LINE");
					geraCodigo("PD	/100");
				}
				else if (isInput) {
					geraCodigo("GD =0");
					if (getSimboloByEscopoIdentificador(idAtual.pop()) != null) {
						sym = getSimboloByEscopoIdentificador(idAtual.pop());
					}
					else {
						new ErroSemantico("Variavel "+sintatico.getToken().getLexeme()+" nao declarada.", sintatico.getLexico().getLineNumber(),this);
					}
					geraCodigo("MM " + sym.getEscopoIdentificador());
				}
				else {
					if (isNumeric(pilhaOperandos.peek())) {
						geraCodigo("LV /" + pilhaOperandos.pop());
					}
					else {
						geraCodigo("LD " + pilhaOperandos.pop());
					}
					sym = getSimboloByName(idAtual.pop());
					geraCodigo("MM " + sym.getEscopoIdentificador());
				}
				isInput = false;
				isPrint = false;
				break;

			/*
			 * Comando IF e ELSE
			 */
			case 201: // Pega o IF
				ifCount++;
				break;
			case 227: // Pega >
			case 228: // Pega <
			case 229: // Pega ==
			case 230: // Pega <>
			case 231: // Pega >=
			case 232: // Pega <=
				if (isNumeric(pilhaOperandos.peek())) {
					geraCodigo("IF_"+ifCount +" LV /" + pilhaOperandos.pop());
				}
				else {
					geraCodigo("IF_"+ifCount +" LD " + pilhaOperandos.pop());
				}
				geraCodigo("MM ARG_COND_1");
				tipoOperacao = sintatico.getToken().getTag();
				break;
			case 234: // Pega o )
				if (isNumeric(pilhaOperandos.peek())) {
					geraCodigo("LV /" + pilhaOperandos.pop());
				}
				else {
					geraCodigo("LD " + pilhaOperandos.pop());
				}
				geraCodigo("MM ARG_COND_2");
				// gerar codigo da comparacao levando em conta tipoOperacao
				geraInicioIF(tipoOperacao);
				escopoAtual.push("IF_"+ifCount);
				break;
			case 237:
//				geraCodigo("// " + escopoAtual);
				escAtual = escopoAtual.peek();
				// gerar jump para END_IF_(count)
				String endIfAtual = pilhaControleIf.peek();
				geraCodigo("JP " + endIfAtual);
				nextLabel = "AUX_" + endIfAtual.substring(endIfAtual.length()-1, endIfAtual.length());
				geraCodigo(nextLabel + " LV /0");
				nextLabel = "";
				
				/*
				 * Dá uma espiada no proximo Token, se nao for ELSE, ou seja, se o if é sozinho
				 * adiciona o fim do IF, se nao ele trata o ELSE normalmente
				 */
				nextLexeme = sintatico.getLexico().peekNextToken().getLexeme();
				if (!nextLexeme.equalsIgnoreCase("ELSE")) {
					endIfAtual = pilhaControleIf.pop();// Termina o IF pra sempre, pois nao tem ELSE
					geraCodigo(endIfAtual+" LV /0");
				}
				break;
				
			/*
			 * Comando ELSE 
			 */
			case 238: // Pega o ELSE
				escopoAtual.push("ELSE_"+ifCount);
				pilhaControleIf.push("ELSE_"+ifCount);
				break;
				
			/*
			 * Comando WHILE
			 */
			case 202: // Pega o WHILE
				whileCount++;
				nextLabel = "WHILE_" + whileCount;
				programaOutput.append(nextLabel + " ");
				nextLabel = "";
				break;
				
			case 243: // Pega >
			case 244: // Pega <
			case 245: // Pega ==
			case 246: // Pega <>
			case 247: // Pega >=
			case 248: // Pega <=
				if (isNumeric(pilhaOperandos.peek())) {
					geraCodigo(" LV /" + pilhaOperandos.pop());
				}
				else {
					geraCodigo(" LD " + pilhaOperandos.pop());
				}
				geraCodigo("MM ARG_COND_1");
				tipoOperacao = sintatico.getToken().getTag();
				break;
				
			case 250: // Pega )
				if (isNumeric(pilhaOperandos.peek())) {
					geraCodigo("LV /" + pilhaOperandos.pop());
				}
				else {
					geraCodigo("LD " + pilhaOperandos.pop());
				}
				geraCodigo("MM ARG_COND_2");
				// gerar codigo da comparacao levando em conta tipoOperacao
				geraInicioWHILE(tipoOperacao);
				nextLabel = "";
				escopoAtual.push("WHILE_"+whileCount);
				break;
				
			case 241:
//				geraCodigo("// " + escopoAtual);
				escAtual = escopoAtual.peek();
				if (pilhaControleWhile.size()>0) {
					// gerar jump para WHILE_(count)
					String whileAtual = pilhaControleWhile.pop();
					geraCodigo(" JP " + whileAtual);
					nextLabel = whileAtual;					
					geraCodigo(nextLabel + " LV /0");
				} 
				else if (pilhaControleIf.size()>0) {
					pilhaControleIf.pop(); // Joga o ELSE de controle fora...
					String endElseAtual = pilhaControleIf.pop();
					nextLabel = endElseAtual;					
					geraCodigo(nextLabel + " LV /0");
				} 
				nextLabel = "";
				//system.out.print(nextLabel + " ");
				break;
			
			/*
			 * Comando INPUT
			 */
			case 203: // Pegou um "input"
				isInput = true;
				break;
			case 213: // Pega o id
				idAtual.push(sintatico.getToken().getLexeme());
//				System.out.println(escopoAtual.peek()+"_"+sintatico.getToken().getLexeme());
				break;
				
				
			/*
			 * Comando PRINT
			 */
			case 204: // Pegou um "print"
				isPrint = true;
				break;
				
			/*
			 * Comando de declaracao de variáveis
			 */
			case 205: // pegou um tipo int
				tipoAtual = Type.Int;
				break;
			case 206: // pegou um tipo float
				tipoAtual = Type.Float;
				new ErroSemantico("Esse compilador nao faz tratamento de tipo " + tipoAtual.getLexeme(), sintatico.getLexico().getLineNumber(),this);
				break;
			case 207: // pegou um tipo char
				tipoAtual = Type.Char;
				new ErroSemantico("Esse compilador nao faz tratamento de tipo " + tipoAtual.getLexeme(), sintatico.getLexico().getLineNumber(),this);
				break;
			case 208: // pegou um tipo boolean
				tipoAtual = Type.Bool;
				new ErroSemantico("Esse compilador nao faz tratamento de tipo " + tipoAtual.getLexeme(), sintatico.getLexico().getLineNumber(),this);
				break;
			case 215: // Pegou "id"
				idAtual.push(sintatico.getToken().getLexeme());
				break;
			case 218: // declara e coloca na tabela de simbolos
				if (isVetor) {
					sym = new Simbolo(idAtual.pop(), escopoAtual.peek(), tipoAtual, true, tamanhoVetor);
				}
				else {
					sym = new Simbolo(idAtual.pop(), escopoAtual.peek(), tipoAtual, false);
				}
				// verifica se já foi declarada no escopoAtual
				if (!foiDeclaradaNoEscopoAtual(sym.getIdentificador())) {
					memoria = new Memoria(sym.getIdentificador(), escopoAtual.peek());
					memoriaUsada.push(memoria);
					tabelaSimbolos.push(sym);
				}
				else {
					//geraCodigo(sym.getIdentificador());
					new ErroSemantico("Declaração duplicada de variável", sintatico.getLexico().getLineNumber(),this);
				}
				isVetor = false;
				break;
				
//			/*
//			 * Comando para declaracao de vetores
//			 */
//			case 13: // Pega [
//				tamanhoVetor = 1;
//				isVetor = true;
//				break;
//			case 16: // Pega , 
//				tamanhoVetor++;
//				break;
//			case 17: // Pega ]
//				break;
				
			/************************************************************
			 * Executa as acoes semanticas para a submaquina "Expressao"
			 ************************************************************/
			case 400: // pega o valor do id
				if (sintatico.getLexico().peekNextToken().getLexeme().equals("(")) { // chamada de sub-rotina
					sym = getSimboloByName(sintatico.getToken().getLexeme());
					chamaFuncao = sym.getIdentificador();
					pilhaOperandos.push(chamaFuncao+"_"+chamaFuncaoCont);
//					pilhaChamadaFuncao.push(chamaFuncao+"_"+chamaFuncaoCont);
					chamaFuncaoCont++;
				}
				else {
					programaOutput.append(nextLabel + " ");
					////system.out.print(nextLabel + " ");
					nextLabel = "";
					if (foiDeclaradaNoEscopoAtual(sintatico.getToken().getLexeme())) {
						sym = getSimboloByName(sintatico.getToken().getLexeme());
					}
					else {
						new ErroSemantico("Variavel não declarada", sintatico.getLexico().getLineNumber(), this);
					}
					pilhaOperandos.push(sym.getEscopoIdentificador());
				}
				break;
			case 401: // pega um num
				nextLexeme = sintatico.getLexico().peekNextToken().getLexeme();
				numeroAtual = sintatico.getToken().getLexeme();
				programaOutput.append(nextLabel + " ");
				nextLabel = "";
				if ((pilhaOperadores.size() > 0 && opAnteriorehMenos())
						&& (!nextLexeme.equals("/") && !nextLexeme.equals("*") && !nextLexeme.equals("**"))) {
					// Fazer a precedencia do menos
					String operando_aux = pilhaOperandos.pop();
					pilhaOperandos.push(numeroAtual);
					pilhaOperandos.push(operando_aux);
				}
				else {
					pilhaOperandos.push(numeroAtual);
				}
				break;
			case 402: // "("
				programaOutput.append(nextLabel + " ");
				nextLabel = "";
				pilhaOperadores.push(sintatico.getToken().getLexeme());
				break;
			case 405: // "*"
			case 406: // "/"
			case 407: // "**"
			case 408: // "+"
			case 409: // "-"
			case 410: // "*"
			case 411: // "/"
			case 412: // "**"
			case 413: // "+"
			case 414: // "-"
//				if (!pilhaOperandos.contains("(")) {
//					new ErroSemantico("Eh preciso colocar \"(\" quando for fazer uma expressao artimetica", sintatico.getLexico().getLineNumber(),this);					
//				}
			case 419: // ")"
			case 423: // ")"
				if (operadoresHasAbreParentesis()) {
					calculaOperacaoAritmetica(sintatico.getToken().getLexeme());
				}
				else {
					new ErroSintatico("Para expressoes, deve-se comecar com (", sintatico.getLexico().getLineNumber());
				}
				break;
				
			/*
			 * Chamada de sub-rotina
			 */
			case 418: // Pega o id da funcao
				geraCodigo("SC "+chamaFuncao);
//				geraCodigo("LD RETORNO_FC");
				geraCodigo("MM "+chamaFuncao+"_"+chamaFuncaoCont);
				pilhaOperandos.push(chamaFuncao+"_"+chamaFuncaoCont);
				pilhaChamadaFuncao.push(chamaFuncao+"_"+chamaFuncaoCont);
				break;
				
			/************************************************************
			 * Executa as acoes semanticas para a submaquina "Funcao"
			 ************************************************************/
				
			case 600: // Pega int
				tipoRetorno = Type.Int;
				break;
			case 601: // Pega float
				tipoRetorno = Type.Float;
				new ErroSemantico("Esse compilador nao faz tratamento de tipo " + tipoAtual.getLexeme(), sintatico.getLexico().getLineNumber(),this);
				break;
			case 602: // pega char
				tipoRetorno = Type.Char;
				new ErroSemantico("Esse compilador nao faz tratamento de tipo " + tipoAtual.getLexeme(), sintatico.getLexico().getLineNumber(),this);
				break;
			case 603: // pega boolean
				tipoRetorno = Type.Bool;
				new ErroSemantico("Esse compilador nao faz tratamento de tipo " + tipoAtual.getLexeme(), sintatico.getLexico().getLineNumber(),this);
				break;
			case 604: // pega void
				tipoRetorno = Type.Void;
				break;
			case 605: // Pega o id
				idAtual.push(sintatico.getToken().getLexeme());
				escopoAtual.push(sintatico.getToken().getLexeme());
				hasParameters = false;
				
				nomeFuncao = idAtual.peek();
//				pilhaChamadaFuncao.push(nomeFuncao);
				if (foiDeclaradaNoEscopoAtual(idAtual.peek())) {
					new ErroSemantico("Funcao ja declarada", sintatico.getLexico().getLineNumber(),this);
				}
				geraCodigo(idAtual.peek() + " JP /000");
				break;
			case 607: // Pegou int
				tipoAtual = Type.Int;
				break;
			case 608: // pega float
				tipoAtual = Type.Float;
				new ErroSemantico("Esse compilador nao faz tratamento de tipo " + tipoAtual.getLexeme(), sintatico.getLexico().getLineNumber(),this);
				break;
			case 609: // pega char
				tipoAtual = Type.Char;
				new ErroSemantico("Esse compilador nao faz tratamento de tipo " + tipoAtual.getLexeme(), sintatico.getLexico().getLineNumber(),this);
				break;
			case 610: // pega boolean
				tipoAtual = Type.Bool;
				new ErroSemantico("Esse compilador nao faz tratamento de tipo " + tipoAtual.getLexeme(), sintatico.getLexico().getLineNumber(),this);
				break;
			case 612: // Pega id
				idAtual.push(sintatico.getToken().getLexeme());
				break;
			case 613: // Pega o {
				if (tipoRetorno == Type.Void) {
					if (hasParameters) {
						// declara funcao void com argumento
						sym = new Simbolo(idAtual.pop(), escopoAtual.peek(), listaArgumentos);
					}
					else {
						// declara funcao void sem argumentos
						sym = new Simbolo(idAtual.pop(), escopoAtual.peek());
					}
				}
				else {
					if (hasParameters) {
						// declara funcao void com argumento
						sym = new Simbolo(idAtual.pop(), escopoAtual.peek(), tipoRetorno, listaArgumentos);
					}
					else {
						// declara funcao void sem argumentos
						sym = new Simbolo(idAtual.pop(), escopoAtual.peek(), tipoRetorno);
					}
				}
				hasParameters = false;
				// verifica se já foi declarada no escopoAtual
				memoria = new Memoria(sym.getIdentificador(), escopoAtual.peek());
				memoriaUsada.push(memoria);
				tabelaSimbolos.push(sym);
				// reseta variaveis setadas
				listaArgumentos = null;
				break;
			case 614:// Pega VOID FINAL
				if (tipoRetorno != Type.Void) {
					new ErroSemantico("Tipagem errada.", sintatico.getLexico().getLineNumber(), this);
				}
				break;
			case 616:// Pega RETURN FINAL
				if (tipoRetorno == Type.Void) {
					new ErroSemantico("Tipagem errada.", sintatico.getLexico().getLineNumber(), this);
				}
				break;
			case 617: // Pega o ;
				if (tipoRetorno == Type.Void) {
                    geraCodigo("RS "+nomeFuncao);
                }
                else {
                    if (isNumeric(pilhaOperandos.peek())) {
                    	geraCodigo("LV /" + pilhaOperandos.pop());
                    	geraCodigo("MM RETORNO_FC");
						geraCodigo("LD RETORNO_FC");
						geraCodigo("RS "+nomeFuncao);
					}
					else {
						geraCodigo("LD " + pilhaOperandos.pop());
						geraCodigo("MM RETORNO_FC");
						geraCodigo("LD RETORNO_FC");
						geraCodigo("RS "+nomeFuncao);
					}
                }
				break;
			case 620: // Pega ,
			case 621: // Pega ( - quando tem parametros
				listaArgumentos.add(new Simbolo(idAtual.pop(), escopoAtual.peek(), tipoAtual, false));
				hasParameters = true;
				break;
			case 623: // Pegou int
				tipoAtual = Type.Int;
				break;
			case 624: // pega float
				tipoAtual = Type.Float;
				new ErroSemantico("Esse compilador nao faz tratamento de tipo " + tipoAtual.getLexeme(), sintatico.getLexico().getLineNumber(),this);
				break;
			case 625: // pega char
				tipoAtual = Type.Char;
				new ErroSemantico("Esse compilador nao faz tratamento de tipo " + tipoAtual.getLexeme(), sintatico.getLexico().getLineNumber(),this);
				break;
			case 626: // pega boolean
				tipoAtual = Type.Bool;
				new ErroSemantico("Esse compilador nao faz tratamento de tipo " + tipoAtual.getLexeme(), sintatico.getLexico().getLineNumber(),this);
				break;
			default:
//				System.out.println("Nao fez acao Semantica");
			}
		}
	}
 
	private boolean operadoresHasAbreParentesis() {
		if (pilhaOperadores.contains("(")) {
			return true;
		}
		else {
			return false;
		}
	}

	/**
	 * Verifica se o ultimo operador aritmetico da pilha eh o sinal de menos
	 * @return
	 */
	private boolean opAnteriorehMenos() {
		boolean existe = false;
		if (pilhaOperadores.size() > 0) {
			int i = pilhaOperadores.size() - 1;
			String opAux = null;
			do {
				opAux = pilhaOperadores.get(i--);
				if (opAux.equals("-")) {
					existe = true;
					break;
				}
			} while (i > 0);
			return existe;
		} else {
			return false;
		}
	}
	
	private void geraInicioWHILE(int tipoOperacao) {
		if (tipoOperacao == Tag.GT) { // >
			geraCodigo("LD ARG_COND_1");
			geraCodigo(" - ARG_COND_2");
			geraCodigo("JN END_WHILE_" + whileCount);
			geraCodigo("JZ END_WHILE_" + whileCount);
		}
		else if (tipoOperacao == Tag.LT) { // <
			geraCodigo("LD ARG_COND_2");
			geraCodigo(" - ARG_COND_1");
			geraCodigo("JN END_WHILE_" + whileCount);
			geraCodigo("JZ END_WHILE_" + whileCount);
		}
		else if (tipoOperacao == Tag.GE) { // >=
			geraCodigo("LD ARG_COND_1");
			geraCodigo(" - ARG_COND_2");
			geraCodigo("JN END_WHILE_" + whileCount);
		}
		else if (tipoOperacao == Tag.LE) { // <=
			geraCodigo("LD ARG_COND_2");
			geraCodigo(" - ARG_COND_1");
			geraCodigo("JN END_WHILE_" + whileCount);
		}
		else if (tipoOperacao == Tag.EQ) { // ==
			geraCodigo("LD ARG_COND_1");
			geraCodigo(" - ARG_COND_2");
			geraCodigo("JZ ACERTO");
			geraCodigo("JP END_WHILE_" + whileCount);
			geraCodigo("ACERTO OS=0");
		}
		else if (tipoOperacao == Tag.NE) { // <>
			geraCodigo("OS = 0");
			geraCodigo("LD ARG_COND_1");
			geraCodigo(" - ARG_COND_2");
			geraCodigo("JZ END_WHILE_" + whileCount);
		}
		pilhaControleWhile.push("END_WHILE_"+whileCount);
	}

	private void geraInicioIF(int tipoOperacao) {
		if (tipoOperacao == Tag.GT) { // >
			geraCodigo("LD ARG_COND_1");
			geraCodigo(" - ARG_COND_2");
			geraCodigo("JN AUX_"+ifCount);
			geraCodigo("JZ AUX_"+ifCount);
		}
		else if (tipoOperacao == Tag.LT) { // <
			geraCodigo("LD ARG_COND_2");
			geraCodigo(" - ARG_COND_1");
			geraCodigo("JN AUX_"+ifCount);
			geraCodigo("JZ AUX_"+ifCount);
		}
		else if (tipoOperacao == Tag.GE) { // >=
			geraCodigo("LD ARG_COND_1");
			geraCodigo(" - ARG_COND_2");
			geraCodigo("JN AUX_"+ifCount);
		}
		else if (tipoOperacao == Tag.LE) { // <=
			geraCodigo("LD ARG_COND_2");
			geraCodigo(" - ARG_COND_1");
			geraCodigo("JN AUX_"+ifCount);
		}
		else if (tipoOperacao == Tag.EQ) { // ==
			geraCodigo("LD ARG_COND_1");
			geraCodigo(" - ARG_COND_2");
			geraCodigo("JZ ACERTO_"+ifCount);
			geraCodigo("JP AUX_"+ifCount);
			geraCodigo("ACERTO_"+ifCount + " LV /0");
		}
		else if (tipoOperacao == Tag.NE) { // <>
			geraCodigo("LD ARG_COND_1");
			geraCodigo(" - ARG_COND_2");
			geraCodigo("JN AUX_"+ifCount);
			geraCodigo("JZ AUX_"+ifCount);
		}
		pilhaControleIf.push("END_IF_"+ifCount);
	}
	
	private Simbolo getSimboloByEscopoIdentificador(String _id) {
		for (int i = tabelaSimbolos.size() - 1; i >= 0; i--) {
			Simbolo sym = tabelaSimbolos.get(i);
//			System.out.println(sym.getIdentificador());
			if (sym.getEscopoIdentificador().equals(_id))
				return sym;
		}
		return null;
	}

	private Simbolo getSimboloByName(String _id) {
		for (int i = tabelaSimbolos.size() - 1; i >= 0; i--) {
			Simbolo sym = tabelaSimbolos.get(i);
//			System.out.println(sym.getIdentificador());
			if (sym.getIdentificador().equals(_id))
				return sym;
		}
		return null;
	}
	
	public Simbolo getSimboloByNameEscopo(String _id, String escopo) {
		if (tabelaSimbolos.size() > 0) {
			int i = tabelaSimbolos.size() - 1;
			Simbolo sym = tabelaSimbolos.get(i--);
			while (sym.getEscopo().equals(escopo) && i >= 0) {
				if (sym.getIdentificador().equals(_id))
					return sym;
				sym = tabelaSimbolos.get(i--);

			}
			return null;
		} else
			return null;
	}

	private boolean foiDeclaradaNoEscopoAtual(String _id) {
        boolean existe = false;
        if (tabelaSimbolos.size() > 0) {
                int i = tabelaSimbolos.size() - 1;
                Simbolo symAux = null;
                do {
                        symAux = tabelaSimbolos.get(i--);
                        if (symAux.getIdentificador().equals(_id)) {
                                existe = true;
                                break;
                        }
                } while (symAux.getEscopo().equals(escopoAtual) && i > 0);
                return existe;
        } else
                return false;
}
	
	private static final boolean isNumeric(final String s) {
		final char[] numbers = s.toCharArray();
		for (int x = 0; x < numbers.length; x++) {
			final char c = numbers[x];
			if ((c >= '0') && (c <= '9'))
				continue;
			return false; // invalid
		}
		return true; // valid
	}
	
	private void calculaOperacaoAritmetica(String operador) {
		if (operador.equals("**"))
			new ErroSemantico("Operador ** nao implementado", sintatico.getLexico().getLineNumber(),this);
		
		if (operador.equals(")")) {
//			geraCodigo("// pilha operadores: " + pilhaOperadores);
//			geraCodigo("// pilha operandos: " + pilhaOperandos);
			if (pilhaOperadores.size() > 0) {
				while (pilhaOperadores.size() > 0
						&& !pilhaOperadores.peek().equals("(")) {
					operando1 = pilhaOperandos.pop();
					operando2 = pilhaOperandos.pop();
					if (isNumeric(operando1)) {
						geraCodigo("LV /" + operando1);
						geraCodigo("MM _OP1");
						operando1 = "_OP1";
					}
					if (isNumeric(operando2)) {
						geraCodigo("LV /" + operando2);
						geraCodigo("MM _OP2");
						operando2 = "_OP2";
					}
					geraCodigo("LD " + operando1);
					geraCodigo(pilhaOperadores.pop() + " " + operando2);
					geraCodigo("MM TMP_" + String.valueOf(contador_operando_tmp));
					memoria = new Memoria("TMP_"
							+ String.valueOf(contador_operando_tmp), "");
					memoriaUsada.push(memoria);
					pilhaOperandos.push("TMP_"
							+ String.valueOf(contador_operando_tmp++));
				}
				if (pilhaOperadores.size() > 0
						&& pilhaOperadores.peek().equals("(")) {
					pilhaOperadores.pop();
				}
			} else {
				operando1 = pilhaOperandos.pop();
				if (isNumeric(operando1)) {
					geraCodigo("LV /" + operando1);
				} else {
					geraCodigo("LD " + operando1);
				}
			}

		} else {
			if (operador.equals("*") || operador.equals("/")) {
				if (pilhaOperadores.size() > 0) {
					if (pilhaOperadores.peek().equals("+") || pilhaOperadores.peek().equals("-")) {
						pilhaOperadores.push(operador);
					} 
					else if (pilhaOperadores.peek().equals("*") || pilhaOperadores.peek().equals("/")) {
						operando1 = pilhaOperandos.pop();
						operando2 = pilhaOperandos.pop();
						if (isNumeric(operando1)) {
							geraCodigo("LV /" + operando1);
							geraCodigo("MM _OP1");
							operando1 = "_OP1";
						}
						if (isNumeric(operando2)) {
							geraCodigo("LV /" + operando2);
							geraCodigo("MM _OP2");
							operando2 = "_OP2";
						}
						geraCodigo("LD " + operando1);
						geraCodigo(pilhaOperadores.pop() + " " + operando2);
						geraCodigo("MM TMP_"
								+ String.valueOf(contador_operando_tmp));
						memoria = new Memoria("TMP_"
								+ String.valueOf(contador_operando_tmp), "");
						memoriaUsada.push(memoria);
						pilhaOperandos.push("TMP_"
								+ String.valueOf(contador_operando_tmp++));
						pilhaOperadores.push(operador);
					} 
					else
						pilhaOperadores.push(operador);
				} 
				else
					pilhaOperadores.push(operador);
			} 
			else if (operador.equals("+") || operador.equals("-")) {
				if (pilhaOperadores.size() > 0) {
					if (pilhaOperadores.peek().equals("+")
							|| pilhaOperadores.peek().equals("-")) {
						operando1 = pilhaOperandos.pop();
						operando2 = pilhaOperandos.pop();
						if (isNumeric(operando1)) {
							geraCodigo("LV /" + operando1);
							geraCodigo("MM _OP1");
							operando1 = "_OP1";
						}
						if (isNumeric(operando2)) {
							geraCodigo("LV /" + operando2);
							geraCodigo("MM _OP2");
							operando2 = "_OP2";
						}
						geraCodigo("LD " + operando1);
						geraCodigo(pilhaOperadores.pop() + " " + operando2);
						geraCodigo("MM TMP_"
								+ String.valueOf(contador_operando_tmp));
						memoria = new Memoria("TMP_"
								+ String.valueOf(contador_operando_tmp), "");
						memoriaUsada.push(memoria);
						pilhaOperandos.push("TMP_"
								+ String.valueOf(contador_operando_tmp++));
						pilhaOperadores.push(operador);
					} else if (pilhaOperadores.peek().equals("*")
							|| pilhaOperadores.peek().equals("/")) {
						// VERIFICAR SE EH VALOR IMEDIATO E USAR LV EM VEZ
						// DE LD EM CASO AFIRMATIVO
						operando1 = pilhaOperandos.pop();
						operando2 = pilhaOperandos.pop();
						if (isNumeric(operando1)) {
							geraCodigo("LV /" + operando1);
							geraCodigo("MM _OP1");
							operando1 = "_OP1";
						}
						if (isNumeric(operando2)) {
							geraCodigo("LV /" + operando2);
							geraCodigo("MM _OP2");
							operando2 = "_OP2";
						}
						geraCodigo("LD " + operando1);
						geraCodigo(pilhaOperadores.pop() + " " + operando2);
						geraCodigo("MM TMP_"
								+ String.valueOf(contador_operando_tmp));
						memoria = new Memoria("TMP_"
								+ String.valueOf(contador_operando_tmp), "");
						memoriaUsada.push(memoria);
						pilhaOperandos.push("TMP_"
								+ String.valueOf(contador_operando_tmp++));
						if (pilhaOperadores.size() > 0
								&& (pilhaOperadores.peek().equals("+") || pilhaOperadores
										.peek().equals("-"))) {
							operando1 = pilhaOperandos.pop();
							operando2 = pilhaOperandos.pop();
							if (isNumeric(operando1)) {
								geraCodigo("LV /" + operando1);
								geraCodigo("MM _OP1");
								operando1 = "_OP1";
							}
							if (isNumeric(operando2)) {
								geraCodigo("LV /" + operando2);
								geraCodigo("MM _OP2");
								operando2 = "_OP2";
							}
							geraCodigo("LD " + operando1);
							geraCodigo(pilhaOperadores.pop() + " " + operando2);
							geraCodigo("MM TMP_"
									+ String.valueOf(contador_operando_tmp));
							memoria = new Memoria("TMP_"
									+ String.valueOf(contador_operando_tmp), "");
							memoriaUsada.push(memoria);
							pilhaOperandos.push("TMP_"
									+ String.valueOf(contador_operando_tmp++));
						}
						pilhaOperadores.push(operador);

					} else
						pilhaOperadores.push(operador);
				} else
					pilhaOperadores.push(operador);
			}
		}
	}

	public void declaraVariaveis() {
		while (memoriaUsada.size() > 0) {
			memoria = memoriaUsada.pop();
			if (memoria.getVetorSize() > 0) {
				if (!memoria.getEscopo().equals("")) {
					geraCodigo(memoria.getEscopo() + "_"
							+ memoria.getIdentificador() + " $ ="
							+ memoria.getVetorSize() * 2);
				} else {
					geraCodigo(memoria.getIdentificador() + " $ ="
							+ memoria.getVetorSize() * 2);
				}
			} else {
				if (!memoria.getEscopo().equals("")) {
					geraCodigo(memoria.getEscopo() + "_"
							+ memoria.getIdentificador() + " K /0");
				} else {
					geraCodigo(memoria.getIdentificador() + " K /0");
				}
			}
		}
		geraCodigo(";FUNCOES" + pilhaChamadaFuncao);
		while (pilhaChamadaFuncao.size() >0) {
			String nome = pilhaChamadaFuncao.pop();
			geraCodigo(nome+" K /0");
		}
		geraCodigo("ARG_COND_1 K /0");
		geraCodigo("ARG_COND_2 K /0");
		geraCodigo("_OP1 K /0");
		geraCodigo("_OP2 K /0");
		geraCodigo("AUX K /0");
		geraCodigo("STRING_1 K	/53	; S");
		geraCodigo("STRING_2 K	/41	; A");
		geraCodigo("STRING_3 K	/49	; I");
		geraCodigo("STRING_4 K	/44	; D");
		geraCodigo("STRING_5 K	/41	; A");
		geraCodigo("STRING_6  K	/20	; ");
		geraCodigo("STRING_7  K	/3d	; =");
		geraCodigo("NEW_LINE K	/0a	; NEW LINE");
		geraCodigo("ARG_INT		K	/0	; ARG INT");
		geraCodigo("OFFSET K	/30	; OFFSET");
		geraCodigo("RESULT_ASC K	/0	; RESULT ASC");
		geraCodigo("RETORNO_FC K /0");
		geraCodigo("");
	}
	
	public void criaMaravilhosoArquivoASM() {
//		System.out.println("********************************************************************");
//		System.out.println("O Arquivo final esta assim: ");
		System.out.println(programaOutput);
//		System.out.println("********************************************************************");
		File progFile = new File(this.fileName.substring(this.fileName.length()-4,this.fileName.length()).concat("asm"));
		Writer output = null;
		try {
			// use buffering
			// FileWriter always assumes default encoding is OK!
			output = new BufferedWriter(new FileWriter(progFile));
			output.write(programaOutput.toString());
		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			// flush and close both "output" and its underlying FileWriter
			if (output != null)
				try {
					output.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}
}
